<?php
namespace app;
use app\core\Config;

class MathExpression
{
    public static function evaluate($expression)
    {
        $config = Config::get('app');
        if ($config['maths'] == 'eval') {
            $code = 'return ' . $expression . ';';
            $result = eval ($code);
            return $result;
        }
        $tokens = self::tokenize($expression);
        $result = self::parseExpression($tokens);
        return $result;
    }

    private static function tokenize($expression)
    {
        $pattern = '/(\d+|\+|\-|\*|\/|\(|\))/';
        preg_match_all($pattern, $expression, $matches);
        return $matches[0];
    }

    private static function parseExpression(&$tokens)
    {
        $values = [];
        $operators = [];

        while (count($tokens) > 0) {
            $token = array_shift($tokens);

            if (is_numeric($token)) {
                $values[] = $token;
            } elseif ($token === '(') {
                $values[] = self::parseExpression($tokens);
            } elseif ($token === ')') {
                break;
            } else {
                while (count($operators) > 0 && self::precedence($operators[count($operators) - 1]) >= self::precedence($token)) {
                    self::applyOperator(array_pop($operators), $values);
                }
                $operators[] = $token;
            }
        }

        while (count($operators) > 0) {
            self::applyOperator(array_pop($operators), $values);
        }

        return array_pop($values);
    }

    private static function precedence($operator)
    {
        if ($operator === '+' || $operator === '-')
            return 1;
        if ($operator === '*' || $operator === '/')
            return 2;
        return 0;
    }

    private static function applyOperator($operator, &$values)
    {
        $right = array_pop($values);
        $left = array_pop($values);

        switch ($operator) {
            case '+':
                $values[] = $left + $right;
                break;
            case '-':
                $values[] = $left - $right;
                break;
            case '*':
                $values[] = $left * $right;
                break;
            case '/':
                if ($right == 0)
                    throw new \Exception('Division by zero');
                $values[] = $left / $right;
                break;
        }
    }
}
